package com.test.routerplugin;

import com.android.SdkConstants;
import com.android.build.api.transform.DirectoryInput;
import com.android.build.api.transform.Format;
import com.android.build.api.transform.JarInput;
import com.android.build.api.transform.QualifiedContent;
import com.android.build.api.transform.Transform;
import com.android.build.api.transform.TransformException;
import com.android.build.api.transform.TransformInput;
import com.android.build.api.transform.TransformInvocation;
import com.android.build.api.transform.TransformOutputProvider;
import com.android.build.gradle.internal.pipeline.TransformManager;
import com.android.utils.FileUtils;

import org.gradle.api.Project;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Opcodes;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
import routerplugin.RegisterCodeGenerator;

/**
 * *******************************************************
 * Author: chinadragon
 * Time: 2023/8/15 10:11
 * Name:  v4-20 - 架构-组件化
 * Overview:
 * Usage: 查看 build目录顺序 intermediates > javac -> transforms -> dex
 * *******************************************************
 */
public class ASMTransform extends Transform {

    //java中javassist、ClassPool、CtClass、Apache CC2链学习
    //https://blog.csdn.net/qq_41874930/article/details/121273650

    //    字节码池子
//    ClassPool mPool = ClassPool.getDefault();

    //    把class字节码加载到内存
    Project project;

    static File destFile;

    static ArrayList<String> registerList = new ArrayList<>();

    public ASMTransform(Project project) {
        this.project = project;
    }


    @Override
    public String getName() {
        return "dragontransform";
    }

    @Override
    public Set<QualifiedContent.ContentType> getInputTypes() {
        return TransformManager.CONTENT_CLASS;
    }

    @Override
    public Set<? super QualifiedContent.Scope> getScopes() {
        return TransformManager.SCOPE_FULL_PROJECT;
    }

    @Override
    public boolean isIncremental() {
        return false;
    }

    @Override
    public void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {
        super.transform(transformInvocation);

        //        准备工作

//   project java 工程  java--class路径  class字节码加载内存


        //查看 build目录顺序 intermediates > javac -> transforms -> dex
        //输入
        Collection<TransformInput> inputs = transformInvocation.getInputs();

        //输出
        TransformOutputProvider outputProvider = transformInvocation.getOutputProvider();


        // 除了APP，其他model是 jar 包
        for (TransformInput input : inputs) {

            /*
                android 四大组件是在系统目录里， 而 AppCompatActivity 是在jar 包里，


                注意：jar 不参与编译，所以jar里面的代码是不能修改的
             */
            //jar 先 copy jar，否则 apk 安装失败，找不到 AppCompatActivity
            for (JarInput jarInput : input.getJarInputs()) {
                File dest = outputProvider.getContentLocation(
                        jarInput.getName(),
                        jarInput.getContentTypes(),
                        jarInput.getScopes(),
                        Format.JAR);

                System.out.println(" === jar dest 目录 : " + dest.getAbsolutePath());
                // === jar dest 目录 : /Users/chinadragon/Desktop/android_project/TransformDemo/app/build/intermediates/transforms/dragontransform/debug/49.jar
                File jarInputFile = jarInput.getFile();

                //处理jar包
                scanJar(jarInputFile, dest);
                FileUtils.copyFile(jarInputFile, dest);// 注意这里是  copyFile
            }


            // app 里的 class
            for (DirectoryInput directoryInput : input.getDirectoryInputs()) {
//                String dirName = directoryInput.getName();
                File file = directoryInput.getFile();
                String preFileName = file.getAbsolutePath();
                System.out.println(" === 输入 目录 : " + preFileName);
                // /Users/chinadragon/Desktop/android_project/TransformDemo/app/build/intermediates/javac/debug/classes

//                try {
//                    mPool.appendClassPath(preFileName);//加载到内存，注意：这个内存是运行内存（mac、window），不是Android内存
//                } catch (NotFoundException e) {
//                    throw new RuntimeException(e);
//                }

                //找到 classes 再 找 Router 类
                findTarget(file, preFileName);

//                //outputProvider 输出
                File dest = outputProvider.getContentLocation(
                        directoryInput.getName(),
                        directoryInput.getContentTypes(),
                        directoryInput.getScopes(),
                        Format.DIRECTORY);

                System.out.println(" === 输出 dest 目录 : " + dest.getAbsolutePath());
                // === 输出 dest 目录 : /Users/chinadragon/Desktop/android_project/TransformDemo/app/build/intermediates/transforms/dragontransform/debug/1
                FileUtils.copyDirectory(file, dest);
            }

        }

        //插桩  shadow 插件化    父类
        if (destFile != null) {
//            找到目的地
            System.out.println("-----------destFile--------");
            RegisterCodeGenerator.insertInitCodeTo(registerList, destFile);
        }

    }

    private void scanJar(File src, File dest) throws IOException {

        JarFile file = new JarFile(src);
        Enumeration<JarEntry> enumeration = file.entries();
        while (enumeration.hasMoreElements()) {
            JarEntry jarEntry = enumeration.nextElement();
            String entryName = jarEntry.getName();
            System.out.println("-------------filePath---------"+entryName);
            if (shouldProcessClass(entryName)) {
//                registerList.add(entryName);
                System.out.println("-------------entryName---------"+entryName);

                InputStream inputStream = file.getInputStream(jarEntry);
                scanClass(inputStream);
            }else if(entryName.equals("com/example/arouter/ARouter.class")) {
                System.out.println("-------------entryName---------"+entryName);
                destFile = dest;
                break;
            }
        }

    }

    private boolean shouldProcessClass(String entryName) {
        String prefix = "com/example/utill/";
        // entryName 示例：com/example/utill/loginRouter.class
        // 后面判断 entryName.length() 大小 ，因为会出现 只有com/example/utill/的情况；
//        return entryName != null && entryName.startsWith(prefix) && entryName.length() > prefix.length();// com.example.utill
        return entryName != null && entryName.startsWith(prefix) && entryName.endsWith(SdkConstants.DOT_CLASS);// com.example.utill
    }

    private void scanClass(InputStream inputStream) throws IOException {
        ClassReader cr = new ClassReader(inputStream);
        ScanClassVisitor cv = new ScanClassVisitor(Opcodes.ASM5);
        cr.accept(cv, ClassReader.EXPAND_FRAMES);
        inputStream.close();
    }


    static class ScanClassVisitor extends ClassVisitor {
        public ScanClassVisitor(int api) {
            super(api);
        }

        @Override
        public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
            super.visit(version, access, name, signature, superName, interfaces);
            if (interfaces != null) {
                for (String interfaceName : interfaces) {
                    System.out.println("---------interfaceName-----" + interfaceName);
                    if (interfaceName.equals("com/example/arouter/IRouter")) {// 只添加实现了 IRouter 接口的类
                        // 记录需要注册的apt生成类
                        if (!ASMTransform.registerList.contains(name)) {
                            ASMTransform.registerList.add(name);
                        }
                    }
                }


            }
        }
    }

    private void findTarget(File clazz, String fileName) {
//        递归寻找到 class 结尾的字节码文件    他们找出来    修改
        if (clazz.isDirectory()) {

            for (File file : clazz.listFiles()) {
                findTarget(file, fileName);
            }

        } else {
//            .class
            modify(clazz, fileName);
        }
    }

    private void modify(File clazz, String fileName) {
        String filePath = clazz.getAbsolutePath();
        if (!filePath.endsWith(SdkConstants.DOT_CLASS)) {
            return;
        }
        if (filePath.contains("R$") || filePath.contains("R.class") || filePath.contains("BuildConfig.class")) {
            return;
        }

        String path = filePath.replace(fileName, "");
        path = path.replaceAll("\\\\", "/");
        System.out.println("-------------filePath---------"+path);
        if (shouldProcessClass(path)) {
            try {
                System.out.println("-------------shouldProcessClass---------"+path);
                //获得IRouterLoad接口实现类，并记录
                scanClass(new FileInputStream(filePath));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}
